﻿using System;
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;

namespace WholeSlideImageLibrary.Formats.ImageSharp
{
    internal sealed class ImageSharpWholeSlideImage : WholeSlideImage
    {
        private Image<Rgba32>? _image;

        public ImageSharpWholeSlideImage(Image<Rgba32> image)
        {
            _image = image;
        }

        public override int LevelCount => 1;

        public override int Width => EnsureNotDisposed().Width;

        public override int Height => EnsureNotDisposed().Height;

        public override System.Drawing.Size GetLevelSize(int level)
        {
            Image<Rgba32>? image = EnsureNotDisposed();
            if (level != 0)
            {
                throw new ArgumentOutOfRangeException(nameof(level));
            }
            return new System.Drawing.Size(image.Width, image.Height);
        }

        public override ValueTask ReadRegionAsync<T>(int level, int x, int y, int width, int height, Memory<T> buffer, CancellationToken cancellationToken)
        {
            Image<Rgba32>? image = EnsureNotDisposed();
            if (level != 0)
            {
                throw new ArgumentOutOfRangeException(nameof(level));
            }
            if (width < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(width));
            }
            if (height < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(height));
            }
            if (width == 0 || height == 0)
            {
                return default;
            }
            if (Unsafe.SizeOf<T>() * buffer.Length < 4 * width * height)
            {
                throw new ArgumentException("destination is too small.", nameof(buffer));
            }

            if (x >= image.Width)
            {
                return default;
            }
            if (y >= image.Height)
            {
                return default;
            }
            if ((x + width) <= 0)
            {
                return default;
            }
            if ((y + height) <= 0)
            {
                return default;
            }

            using var destination = Image.WrapMemory(buffer.Cast<T, Rgba32>(), width, height);
            destination.Mutate(ctx => ctx.DrawImage(image, new Point(-x, -y), opacity: 1));

            return default;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private Image<Rgba32> EnsureNotDisposed()
        {
            Image<Rgba32>? image = _image;
            if (image is null)
            {
                ThrowObjectDisposedException();
            }
            return image;
        }

        [DoesNotReturn]
        private static void ThrowObjectDisposedException()
        {
            throw new ObjectDisposedException(nameof(ImageSharpWholeSlideImage));
        }
    }
}
